﻿/**
 * @author alex
 */

Namespace("sketch.framework");

(function () {
    var fwk = sketch.framework;

    fwk.Container = klass(fwk.UIElement, (function() {
        var hited = function(x, y) {
            return function(idx, element) {
                return element.hitTest(x, y);
            }
        };

        var isLockGroup = function(){
            var view = this.view();
            var selected = false;
            if(view){
                var selectedElement = view.selectedElement();
                if(selectedElement && selectedElement.isDescendantOrSame(this)){
                    selected = true;
                }
            }
            return this._groupContainer && (!this._activeGroup || !selected);
        };

        var delegateToChildren = function(name, event) {
            var pos = eventToLocal.call(this, event);

            this.children.allReverse(hited(pos.x, pos.y), function(idx, element) {
                element[name](event);
                return !event.handled;
            });
        };

        var eventToLocal = function(event) {
            return {
                x:event.x - this.left(),
                y:event.y - this.top()
            };
        };

        var updateChildren = function() {
            this._drawBuffer.width(this.width());
            this._drawBuffer.height(this.height());
            this._drawBuffer.clear();

            var context = this._drawBuffer.context;
            this.children.each(function(idx, child) {
                if (child.visible()) {
                    child.draw(context);
                }
            });
        };

        var containerChanged = function(events) {
            for (var i = 0, length = events.length; i < length; i++) {
                var property = events[i].property;
                if (property !== "left" && property !== "top") {
                    this.invalidate();
                    return;
                }
            }
        };

        var onresize = function() {
            this.performArrange();
        };

        return {
            __type__:"Container",
            _constructor : function(/*bool*/buffering) {
                this.children = new fwk.OrderedCollection();
                this._drawBuffer = new fwk.DrawBuffer();
                this._selectedElement = null;
                this._elementMoveOffset = {x:0, y:0};
                this._mousePosition = {x:0, y:0};
                this._view = null;
                this._buffering = buffering || false;
                this._dropPositioning = "none";

                this._groupContainer = false;
                this._activeGroup = false
                this._allowGroupActivation = true;
                this.onresize.bind(EventHandler(this, onresize));
                this.removing = fwk.EventHelper.createEvent();

                this.properties.createProperty("padding", "Padding", new fwk.Box());

                this._arrangeHandler = EventHandler(this, this.performArrange);
                this.properties.propertyChanged.bind(EventHandler(this, containerChanged));
                if (this._buffering) {
                    this._updateChildrenHandler = EventHandler(this, this.invalidate);
                    this.children.changed.bind(this._updateChildrenHandler);
                }
            },
            lockArrange: function(){
                this._arrangeLocked = true;
            },
            unlockArrange: function(){
                this._arrangeLocked = false;
                if (this._arrangeRequested){
                    this._arrangeRequested = false;
                    this.performArrange();
                }
            },
            performArrange: function() {
                if (this._arrangeLocked){
                    this._arrangeRequested = true;
                    return;
                }
                var oldBoundary = this.getBoundaryRect();
                this.arrange();
                var newBoundary = this.getBoundaryRect();

                if (oldBoundary.width !== newBoundary.width || oldBoundary.height !== newBoundary.height) {
                    this.onresize(newBoundary);
                }
            },
            dragging : function(event) {
                event.handled = true;
            },
            stopDragging : function(event) {
            },
            drawSelf:function(context) {
                fwk.Container.Super.drawSelf.call(this, context);
                if (this._buffering) {
                    this._drawBuffer.draw(context, this.left(), this.top());
                } else {
                    context.save();
                    context.translate(this.left(), this.top());
                    this.children.each(function(idx, child) {
                        if (child.visible()) {
                            child.draw(context);
                        }
                    });
                    context.restore();
                }
            },
            padding: function(value) {
                return this.properties.padding(value);
            },
            add:function(/*UIElement*/element) {
                this.children.add(element);
                element.parent(this);
                if (this._buffering) {
                    if (element.properties) {//TODO: remove this if
                        element.properties.propertyChanged.bind(this._updateChildrenHandler);
                    }
                }
                element.onresize.bind(this._arrangeHandler);
                this.performArrange();
                this.invalidate();
            },
            insert:function(/*UIElement*/element, /*int*/index) {
                element.parent(this);
                if (this._buffering) {
                    if (element.properties) {//TODO: remove this if
                        element.properties.propertyChanged.bind(this._updateChildrenHandler);
                    }
                }
                element.onresize.bind(this._arrangeHandler);
                this.children.insert(element, index);
                this.performArrange();
                this.invalidate();
            },
            changePosition:function(/*UIElement*/element, /*int*/index) {
                if (this._buffering) {
                    if (element.properties) {//TODO: remove this if
                        element.properties.propertyChanged.bind(this._updateChildrenHandler);
                    }
                }
                element.onresize.bind(this._arrangeHandler);
                this.children.changePosition(element, index);
                this.performArrange();
                this.invalidate();
            },
            view:function(/*View*/view) {
                var parent = this.parent();
                if (parent == null) {
                    return null;
                }
                return parent.view();
            },
            remove:function(/*UIElement*/element) {
                var e = {element: element, cancel: false};
                this.removing(e);
                if (e.cancel){
                    return;
                }

                this.children.remove(element);
                element.parent(fwk.NullContainer.instance);
                this.performArrange();
                if (this._buffering) {
                    element.properties.propertyChanged.unbind(this._updateChildrenHandler);
                }
                element.onresize.unbind(this._arrangeHandler);
                this.invalidate();
            },
            clear: function() {
                this.children.clear();
            },
            canAdd: function(element){
                return true;
            },
            canRemove: function(element){
                return true;
            },
            canInsert: function(element, index){
                return true;
            },
            mousedown : function(event) {
                fwk.Container.Super.mousedown.call(this, event); //to hide inplace editor
                delegateToChildren.call(this, "mousedown", event);

                var view = this.view();
                if(view){
                    var selectedElement = view.selectedElement();
                    if(selectedElement && selectedElement.isDescendantOrSame(this)){
                        this._allowGroupActivation = true;
                    }else{
                        this._allowGroupActivation = false;
                        this._activeGroup = false;
                    }
                }
            },
            mousemove : function(event) {
                delegateToChildren.call(this, "mousemove", event);
            },
            mouseup : function(event) {
                delegateToChildren.call(this, "mouseup", event);
            },
            dblclick : function(event) {
                delegateToChildren.call(this, "dblclick", event);
            },
            click:function(event){
                if(this._allowGroupActivation){
                    this._activeGroup = true;
                    var element = this.hitElement(event);
                    if(element !== this && element !== null){
                        this.view().makeSelection([element]);
                    }
                }
            },
            hitElement : function(/*Point*/position) {
                var that = this;
                var hitElement = null;

                this.applyVisitor(function(element) {
                    if (that !== element) {
                        var point = element.parent().global2local(position);
                        if (element.canSelect() && element.hitTest(point.x, point.y)) {
                            hitElement = element;
                            return false;
                        }
                    }
                });

                return hitElement;
            },
            locked:function(){
                var parent = this.parent();
                return isLockGroup.call(this) || parent.locked();
            },
            captureMouse : function(/*UIElement*/element) {
                this.view().captureMouse(element);
            },
            releaseMouse : function(/*UIElement*/element) {
                this.view().releaseMouse(element);
            },
            applyVisitor:function(/*Visitor*/callback) {
                var stop = false;
//                if(!lockGroup.call(this)){
                    this.children.eachReverse(function(i, item) {
                        if (item.applyVisitor(callback) === false) {
                            stop = true;
                            return false;
                        }
                    });
//                }
                if (!stop) {
                    return callback(this);
                } else {
                    return false;
                }
            },
            global2local : function(/*Point*/pos) {
                return {
                    x:this.parent().global2local(pos).x - this.left(),
                    y:this.parent().global2local(pos).y - this.top()
                };
            },
            local2global : function(/*Point*/pos) {
                return {
                    x:this.parent().local2global(pos).x + this.left(),
                    y:this.parent().local2global(pos).y + this.top()
                };
            },
            dropOn : function(event) {
                var element = event.element;

                if(this.view().isElementSelected(element)){
                    this._activeGroup = true;
                }
                event.handled = true;
            },
            invalidate:function() {
                if(!this._lockInvalidate){
                    if (this._buffering === true) {
                        updateChildren.call(this);
                    }
                    fwk.Container.Super.invalidate.call(this);
                }
            },
            arrange:function() {

            },
            dropPositioning:function(value) {
                if (arguments.length === 1) {
                    this._dropPositioning = value;
                }

                return this._dropPositioning;
            },
            enableGroupLocking:function(value) {
                if (arguments.length === 1) {
                    this._groupContainer = value;
                }

                return this._groupContainer;
            },
            getDropData:function(pos, element) {
                var width = this.width(),
                    height = this.height();

                pos = this.global2local(pos);

                var intervals = [];
                var last = 0;
                var baseLine;

                function calculateBaseLine(intervals, pos, insertIndex) {
                    var minY = Number.MAX_VALUE;
                    var lineY = 0;
                    for (var i = 0; i < intervals.length; i++) {
                        var itr = intervals[i];
                        var minCandidate = Math.abs(itr - pos);
                        if (minCandidate < minY) {
                            minY = minCandidate;
                            lineY = itr;
                            insertIndex = i;
                        }
                    }
                    return {lineY:lineY, insertIndex:insertIndex};
                }

                var dropPositioning = this.dropPositioning();
                if (dropPositioning === 'vertical') {
                    this.children.each(function(_, _element) {
                        if (_element !== element) {
                            var gr = _element.getBoundaryRect();
                            intervals.push((last + gr.y) / 2);
                            last = gr.y + gr.height;
                        }
                    });

                    intervals.push((last + height) / 2);

                    baseLine = calculateBaseLine(intervals, pos.y);

                    var pt1 = this.local2global({x:5, y:baseLine.lineY});
                    var pt2 = this.local2global({x:width - 10, y:baseLine.lineY});
                    return {
                        x1:pt1.x,
                        y1:pt1.y,
                        x2:pt2.x,
                        y2:pt2.y,
                        index:baseLine.insertIndex
                    }
                } else if (dropPositioning === 'horizontal') {
                    this.children.each(function(_, _element) {
                        if (_element !== element) {
                            var gr = _element.getBoundaryRect();
                            intervals.push((last + gr.x) / 2);
                            last = gr.x + gr.width;
                        }
                    });

                    intervals.push((last + width) / 2);

                    baseLine = calculateBaseLine(intervals, pos.x);

                    var pt1 = this.local2global({ x:baseLine.lineY, y:5});
                    var pt2 = this.local2global({x:baseLine.lineY, y: height - 10});
                    return {
                        x1:pt1.x,
                        y1:pt1.y,
                        x2:pt2.x,
                        y2:pt2.y,
                        index:baseLine.insertIndex
                    }
                }

                return null;
            },
            
            getChildren: function(){
                return this.children;
            },

            clone: function(){
                var clone = this.shallowCopy();
                clone.clear();

                this.getChildren().each(function(idx, child){
                    clone.add(child.clone());
                });

                return clone;
            }
        };
    })());
})();